#!/usr/bin/env python
# _*_ coding: UTF-8 _*_

import os, select, errno

class Reactor:
    READ, WRITE, EXCEPT = 1, 2, 4
    def __init__(self, ctx):
        self.routetab = {Reactor.READ:[], Reactor.WRITE:[], Reactor.EXCEPT:[]}
        self.lock = ctx["lock"]
        self.logger = ctx["logger"]
        self.shutdown = False
        self.pread, self.pwrite = os.pipe()
        self.register(self, Reactor.READ)
        
    def fileno(self):
        return self.pread

    def handleRead(self):
        message = os.read(self.pread, 4096)
    
    def handleWrite(self):
        assert 0, "BUG!!!"
    
    def handleExcept(self):
        assert 0, "BUG!!!"

    def notify(self, message):
        os.write(self.pwrite, message)

    def register(self, obj, mask):
        if (mask & Reactor.READ) and (not obj in self.routetab[Reactor.READ]):
            self.routetab[Reactor.READ].append(obj)
        if (mask & Reactor.WRITE) and (not obj in self.routetab[Reactor.WRITE]):
            self.routetab[Reactor.WRITE].append(obj)
        if (mask & Reactor.EXCEPT) and (not obj in self.routetab[Reactor.EXCEPT]):
            self.routetab[Reactor.EXCEPT].append(obj)
        self.notify("register")

    def unregister(self, obj, mask):
        if (mask & Reactor.READ) and (obj in self.routetab[Reactor.READ]):
            self.routetab[Reactor.READ].remove(obj)
        if (mask & Reactor.WRITE) and (obj in self.routetab[Reactor.WRITE]):
            self.routetab[Reactor.WRITE].remove(obj)
        if (mask & Reactor.EXCEPT) and (obj in self.routetab[Reactor.EXCEPT]):
            self.routetab[Reactor.EXCEPT].remove(obj)
        self.notify("unregister")
    
    def has(self, obj, mask):
        result = 0
        if (mask & Reactor.READ) and (obj in self.routetab[Reactor.READ]):
            result = result | Reactor.READ
        if (mask & Reactor.WRITE) and (obj in self.routetab[Reactor.WRITE]):
            result = result | Reactor.WRITE
        if (mask & Reactor.EXCEPT) and (obj in self.routetab[Reactor.EXCEPT]):
            result = result | Reactor.EXCEPT
        return result
    
    def stop(self):
        self.shutdown = True
        self.notify("stop")
    
    def stopped(self):
        return self.shutdown
    
    def run(self):
        while not self.shutdown:
            try:
                (rready, wready, eready) = select.select(self.routetab[Reactor.READ], 
                    self.routetab[Reactor.WRITE], self.routetab[Reactor.EXCEPT])
            except select.error, (eno, estr):
                if eno == errno.EINTR or eno == errno.EAGAIN:
                    continue
                else:
                    self.logger.write("select error [%s]%s\n" % (eno, estr))
                    self.shutdown = True
                    continue
            has_event = False
            if rready or wready or eready:
                has_event = True
            if self.lock and has_event:
                self.lock.acquire()
            try:
                for obj in rready:
                    if self.shutdown: break
                    obj.handleRead()
                for obj in wready:
                    if self.shutdown: break
                    obj.handleWrite()
                for obj in eready:
                    if self.shutdown: break
                    obj.handleExcept()
            except (SystemExit, KeyboardInterrupt, Exception), e:
                self.logger.write("reactor exception %s\n" % str(e))
                self.shutdown = True
            finally:
                if self.lock and has_event:
                    self.lock.release()
